#include <iostream>
#include <sys/poll.h>
#include <string>
#include "socket.hpp"
using namespace socket_ns;

class PollServer
{
    const static int gdefaultfd = -1;
    const static int gnum = 1;

public:
    PollServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<TcpSocket>()), _num(0), _capacity(gnum)
    {
        _fd_events = new struct pollfd[gnum];
        _listensock->BuildListenSocket(_port);
    }
    ~PollServer()
    {
    }
    void Init()
    {
        for (int i = 0; i < _capacity; i++)
        {
            _fd_events[i].fd = gdefaultfd;
            _fd_events[i].events = 0;
            _fd_events[i].revents = 0;
        }
        _fd_events[_num].fd = _listensock->Sockfd();
        _fd_events[_num].events = POLLIN;
        _num++;
    }

    void Accepter()
    {
        InetAddr addr;
        int sockfd = _listensock->Accepter(&addr);
        if (sockfd > 0)
        {
            LOG(DEBUG, "get a new link, client info %s:%d\n", addr.IP().c_str(), addr.Port());
            // 扩容
            if (_num == _capacity)
            {
                struct pollfd *tmp = new struct pollfd[_capacity * 2];
                for (int i = 0; i < _capacity * 2; i++)
                {
                    tmp[i].fd = gdefaultfd;
                    tmp[i].events = 0;
                    tmp[i].revents = 0;
                }
                for (int i = 0; i < _num; i++)
                {
                    tmp[i].fd = _fd_events[i].fd;
                    tmp[i].events = _fd_events[i].events;
                    tmp[i].revents = _fd_events[i].revents;
                }
                _capacity = _capacity * 2;
                _fd_events = tmp;
                LOG(INFO, "fd_events is expend!,_capacity:%d\n", _capacity);
            }
            _fd_events[_num].fd = sockfd;
            _fd_events[_num].events = POLLIN;
            _num++;
            LOG(INFO, "_num is %d\n", _num);
            LOG(INFO, "add %d to fd_array success!\n", sockfd);
        }
    }

    void HanderIO(int i)
    {
        char buffer[1024];
        ssize_t n = ::recv(_fd_events[i].fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
            std::string respstr = "HTTP/1.0 200 OK\r\n";
            std::string content = "<html><body><h1>hello</h1></body></html>";
            respstr += "Content-Type: text/html\r\n";
            respstr += "Content-Length: " + std::to_string(content.size()) + "\r\n";
            respstr += "\r\n";
            respstr += content;
            ::send(_fd_events[i].fd, respstr.c_str(), respstr.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit...\n");
            // 关闭fd
            ::close(_fd_events[i].fd);
            _fd_events[i].fd = gdefaultfd;
            for (int j = i; j < _num - 1; j++)
            {
                _fd_events[j] = _fd_events[j + 1];
            }
            --_num;
            LOG(INFO, "_num is %d\n", _num);
        }
        else
        {
            LOG(ERROR, "recv error\n");
            // 关闭fd
            ::close(_fd_events[i].fd);
            _fd_events[i].fd = gdefaultfd;
            for (int j = i; j < _num - 1; j++)
            {
                _fd_events[j] = _fd_events[j + 1];
            }
            --_num;
            LOG(INFO, "_num is %d\n", _num);
        }
    }
    void HandlerEvent()
    {
        for (int i = 0; i < _num; i++)
        {
            if (_fd_events[i].revents & POLLIN)
            {
                if (_listensock->Sockfd() == _fd_events[i].fd)
                {
                    Accepter();
                }
                else
                {
                    HanderIO(i);
                }
            }
        }
    }

    void Loop()
    {
        while (true)
        {
            int timeout = 3000;
            int n = ::poll(_fd_events, _num, timeout);
            switch (n)
            {
            case 0:
                LOG(DEBUG, "time out\n");
                break;
            case -1:
                LOG(ERROR, "poll error\n");
                break;
            default:
                LOG(INFO, "haved event ready, n : %d\n", n);
                HandlerEvent();
                break;
            }
        }
    }

private:
    uint16_t _port;
    SockSPtr _listensock;
    struct pollfd *_fd_events;
    int _num;
    int _capacity;
};
